using Digitalmes.Domain.Entities.Mes.Equipments;

namespace Digitalmes.Application.Scada.Commands;

/// <summary>
/// 设备警报命令处理者。
/// </summary>
internal sealed class EquipmentAlarmCommandHandler(
    IRepository<EquipAlarmItem> alarmItemRepo,
    IRepository<EquipAlarmRecord> alarmRecordRepo) : IRequestHandler<EquipmentAlarmCommand>
{
    public async Task Handle(EquipmentAlarmCommand request, CancellationToken cancellationToken)
    {
        // 对当前警报集合和上一次的警报集合进行对比:
        //  0/1 => 新增警报
        //  1/1 => 不变，表示警报持续中
        //  1/0 => 警报结束，关闭警报
        // 新增警报，先从数据库中查找是否该警报已创建且未关闭（防止因服务重启上一次警报记录丢失导致重复创建）

        List<int> newAlarmNoList = []; // 新的警报索引集合
        List<int> closedAlarmNoList = []; // 要关闭的警报索引集合

        // 第一次警报
        if (request.LastAlarmData.Length == 0)
        {
            // 提取警报的索引号（基地址从 1 开始）
            for (int i = 0; i < request.AlarmData.Length; i++)
            {
                if (request.AlarmData[i])
                {
                    newAlarmNoList.Add(i + 1); // 基地址为 1
                }
            }
        }
        else
        {
            // 对比上一次警报，筛选出要关闭的警报和新增的警报集合。
            for (int i = 0; i < request.AlarmData.Length; i++)
            {
                bool lastAlarm = request.LastAlarmData[i], newAlarm = request.AlarmData[i]; // 警报数量长度一致
                if (lastAlarm == newAlarm)
                {
                    continue;
                }

                // 0/1 => 新增警报
                if (!lastAlarm && newAlarm)
                {
                    newAlarmNoList.Add(i + 1);
                }
                else if (lastAlarm && !newAlarm)  // 1/0 => 警报结束，关闭警报
                {
                    closedAlarmNoList.Add(i + 1);
                }
            }
        }

        var workCenter = request.WsCode ?? request.DeviceName; // 工作中心，优先已工位为主
        // 先关闭待关闭的警报
        if (closedAlarmNoList.Count > 0)
        {
            // 待关闭的警报
            var closingAlarms = await alarmRecordRepo.GetListAsync(s => s.LineCode == request.LineCode
                && s.WorkCenter == workCenter
                && closedAlarmNoList.Contains(s.Index) && s.Status == AlarmStatusEnum.InProgress, cancellationToken);
            if (closingAlarms.Count > 0)
            {
                closingAlarms.ForEach(s =>
                {
                    s.Close();
                });
                await alarmRecordRepo.UpdateAsync(closingAlarms, it => new
                {
                    it.Status,
                    it.EndTime,
                    it.Duration,
                }, cancellationToken);
            }
        }

        // 添加新增的警报
        if (newAlarmNoList.Count > 0)
        {
            // 获取警报定义
            var alarmItems = await alarmItemRepo.GetListAsync(m => m.LineCode == request.LineCode
                && m.WorkCenter == workCenter
                && newAlarmNoList.Contains(m.Index), cancellationToken);

            // 未定义的警报不进行记录
            if (alarmItems.Count == 0)
            {
                return;
            }

            // 检查是否新增的警报在记录中存在且未关闭，若存在这些警报则新增的时候进行排除
            List<int> newAlarmsNoList2 = [.. alarmItems.Select(s => s.Index)];
            var closingAlarms2 = await alarmRecordRepo.GetListAsync(s => s.LineCode == request.LineCode
                && s.WorkCenter == workCenter
                && newAlarmsNoList2.Contains(s.Index) && s.Status == AlarmStatusEnum.InProgress, cancellationToken);
            var newAlarmsList = alarmItems.Where(s => !closingAlarms2.Any(t => t.Index == s.Index))
                .Select(s => new EquipAlarmRecord
                {
                    LineCode = request.LineCode,
                    WorkCenter = workCenter,
                    Index = s.Index,
                    Category = s.Category,
                    Content = s.Content,
                    AlarmLevel = s.AlarmLevel,
                    StartTime = DateTime.Now,
                    Status = AlarmStatusEnum.InProgress,
                }).ToList();

            if (newAlarmsList.Count > 0)
            {
                await alarmRecordRepo.InsertRangeAsync(newAlarmsList, cancellationToken);
            }
        }
    }
}
